home *** CD-ROM | disk | FTP | other *** search
- /*
- File: SoundSecrets.c
-
- Contains: A program to demonstrate cool things you can do with
- the Sound Manager.
-
- Written by: Kip Olson, Apple Computer
- */
-
- #include <Types.h>
- #include <Memory.h>
- #include <Quickdraw.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Packages.h>
- #include <OSUtils.h>
- #include <ToolUtils.h>
- #include <Menus.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <Resources.h>
- #include <Files.h>
- #include <Errors.h>
- #include <Strings.h>
- #include <Sound.h>
- #include <SoundComponents.h>
- #include <SoundInput.h>
- #include <stddef.h>
- #include <stdio.h>
-
- // 'DITL' ID's for main dialog
-
- enum {
- kQuitButton = 1,
- kPlayButton,
- kStopButton,
- kLeftVolumeSlider,
- kLeftVolumeEdit,
- kRightVolumeSlider,
- kRightVolumeEdit,
- kRateSlider,
- kRateEdit,
- kSizeSlider,
- kSizeEdit,
- kLerpCheckBox,
- kHardwareSettingsLabel,
- kSoundSettingsLabel
- };
-
- // constants
-
- enum {
- kDialogResID = 128,
-
- kRequiredSndMgrMajorRev = 0x03, // Sound Manager 3.1 or later required
- kRequiredSndMgrMinorRev = 0x10,
-
- kMinChunkSamples = 128, // smallest buffer size (in samples)
- kVolumeSliderMax = 0x0200, // largest volume slider value (2.0)
- kVolumeEditMax = 0x0A00, // largest volume edit value (10.0)
- kRateSliderMax = 0x00020000, // largest rate multiplier slider value (2.0)
- kRateEditMax = 0x000A0000 // largest rate multiplier edit value (10.0)
- };
-
- // sound header data structure
-
- typedef union {
- SoundHeader s; // plain sound header
- CmpSoundHeader c; // compressed sound header
- ExtSoundHeader e; // extended sound header
- } CommonSoundHeader, *CommonSoundHeaderPtr;
-
- // globals data structure
-
- typedef struct {
- CommonSoundHeader sndHeader; // sound header
- SoundComponentData sndInfo; // sound info
- CompressionInfo compInfo; // compression info
- SoundComponentData hardwareInfo; // hardware info
- SndChannelPtr sndChannel; // sound channel
- Handle sndH; // 'snd ' resource handle
-
- unsigned long headerOffset; // offset to sound header in handle
- unsigned long dataOffset; // offset of audio samples in handle
- unsigned long totalSamples; // total audio samples in handle
- unsigned long sampleOffset; // current sample offset in handle
- UnsignedFixed rateMultiplier; // current rate multiplier
- unsigned short leftVolume; // current left volume
- unsigned short rightVolume; // current right volume
- unsigned long chunkSamples; // current buffer size
- unsigned long minChunkSamples; // minimum buffer size
- } GlobalsRecord, *GlobalsPtr;
-
- // globals
-
- GlobalsRecord myGlobals; // globals used by our window
-
- // prototypes
-
- void main(void);
- Boolean HasValidSoundManager(void);
- OSErr FindMatchingSound(GlobalsPtr globals);
-
- OSErr ParseSnd(Handle sndH, SoundComponentData *sndInfo, CompressionInfo *compInfo,
- unsigned long *headerOffsetResult, unsigned long *dataOffsetResult);
- void SetupDialog(GlobalsPtr globals, DialogPtr dialog);
- OSErr GetCompressionName(OSType compressionType, Str255 compressionName);
- void SoundSettings2String(SoundComponentData *sndInfo, Str255 stringData);
-
- pascal void DoCallback(SndChannelPtr chan, SndCommand *cmd);
- OSErr DoPlay(GlobalsPtr globals);
- OSErr DoStop(SndChannelPtr chan);
-
- OSErr GetHardwareSettings(SndChannelPtr chan, SoundComponentData *hardwareInfo);
- OSErr SetRateMultiplier(SndChannelPtr chan, UnsignedFixed rateMultiplier);
- OSErr GetRateMultiplier(SndChannelPtr chan, UnsignedFixed *rateMultiplier);
- OSErr SetVolume(SndChannelPtr chan, unsigned short leftVolume, unsigned short rightVolume);
- OSErr GetVolume(SndChannelPtr chan, unsigned short *leftVolume, unsigned short *rightVolume);
- OSErr SetLinearInterpolation(SndChannelPtr chan, short doLerp);
-
- void UpdateSlider(GlobalsPtr globals, DialogPtr dialog, short item);
- void DragSlider(GlobalsPtr globals, DialogPtr dialog, short item);
- pascal void DrawSlider(WindowPtr dialog, short item);
- void GetThumbRect(GlobalsPtr globals, short item, Rect *bounds, Rect *thumbRect);
-
- ControlHandle GetItemHandle(DialogPtr dialog, short item);
- void SetItemProc(DialogPtr dialog, short item, UserItemUPP proc);
- void GetItemRect(DialogPtr dialog, short item, Rect *iRect);
- short GetCheckBox(DialogPtr dialog, short item);
- void SetCheckBox(DialogPtr dialog, short item, short value);
-
- extern pascal OSErr SndGetInfo(SndChannelPtr chan, OSType selector, void *infoPtr)
- FOURWORDINLINE(0x203C, 0x063C, 0x0018, 0xA800);
-
- /* main entry point
-
- Remember, this application is meant to show cool things you can do with the
- Sound Manager, not to show good human interface programming. The following
- code to manage the dialog is not meant to be a good example of application
- design.
- */
-
- void main(void)
- {
- OSErr err;
- DialogPtr dialog;
- GrafPtr oldPort;
- short item;
- Boolean done;
- Str255 stringData;
- GlobalsPtr globals = &myGlobals;
- ControlHandle itemHandle;
- float rate;
- unsigned short volume;
- unsigned long chunkSamples;
- short lerpOn;
- NumVersion version;
-
- // initialize the world
-
- InitGraf((Ptr) &qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(nil);
- InitCursor();
- MaxApplZone();
-
- // make sure we have Sound Manager 3.1
-
- if (!HasValidSoundManager())
- {
- err = noHardwareErr;
- goto SndVersionFailed;
- }
-
- // open a Sound Manager channel
-
- globals->sndChannel = nil; // tell Sound Manager to allocate the sound channel structure
- err = SndNewChannel(&globals->sndChannel, sampledSynth, 0, DoCallback); // allocate a sound channel with a callback routine
- if (err != noErr)
- goto SndNewChannelFailed;
-
- // get the sound hardware settings
-
- err = GetHardwareSettings(globals->sndChannel, &globals->hardwareInfo);
- if (err != noErr)
- goto GetSettingsFailed;
-
- // find a sound resource that matches these hardware settings
-
- err = FindMatchingSound(globals);
- if (err != noErr)
- goto MatchingSoundNotFound;
-
- // setup globals to play this sound
-
- HLock(globals->sndH);
- globals->sndHeader = *((CommonSoundHeaderPtr) (*globals->sndH + globals->headerOffset)); // use sound header from resource
- globals->sndHeader.s.loopStart = 0; // clear loop points
- globals->sndHeader.s.loopEnd = 0;
-
- GetRateMultiplier(globals->sndChannel, &globals->rateMultiplier); // get initial rate multiplier setting
- GetVolume(globals->sndChannel, &globals->leftVolume, &globals->rightVolume);// get initial volume settings
- globals->sampleOffset = 0; // start playing at first sample
- globals->totalSamples = globals->sndInfo.sampleCount; // total no. samples in sound
- globals->chunkSamples = globals->totalSamples; // make chunk the size of the sound
-
- // If the chunk size is too small, the Sound Manager will spend too much time
- // servicing the interrupt, so we need to limit the size of the smallest chunk.
- // We do this by quantizing a constant to a multiple of the packet size of the
- // sound and pinning the chunk size to this minimum.
-
- globals->minChunkSamples = (kMinChunkSamples / globals->compInfo.samplesPerPacket) * globals->compInfo.samplesPerPacket;
-
- // setup main window
-
- GetPort(&oldPort);
-
- dialog = GetNewDialog(kDialogResID, nil, (WindowPtr) -1);
- if (dialog == nil)
- {
- err = paramErr;
- goto NewDialogFailed;
- }
-
- SetPort(dialog);
- SetupDialog(globals, dialog);
- ShowWindow(dialog);
-
- // main event loop
-
- done = false;
- while (!done)
- {
- ModalDialog(nil, &item);
-
- switch (item)
- {
- case kQuitButton:
- done = true;
- break;
-
- case kPlayButton:
- DoStop(globals->sndChannel);
- globals->sampleOffset = 0;
- DoPlay(globals);
- break;
-
- case kStopButton:
- DoStop(globals->sndChannel);
- break;
-
- case kLeftVolumeSlider:
- case kRightVolumeSlider:
- case kRateSlider:
- case kSizeSlider:
- DragSlider(globals, dialog, item);
- break;
-
- case kLeftVolumeEdit:
- case kRightVolumeEdit:
- itemHandle = GetItemHandle(dialog, item);
- GetIText((Handle) itemHandle, stringData);
-
- p2cstr(stringData);
- sscanf((char *) stringData, "%f", &rate);
- volume = rate * 256L;
- if (volume > kVolumeEditMax)
- volume = kVolumeEditMax;
-
- if (item == kLeftVolumeEdit)
- {
- globals->leftVolume = volume;
- UpdateSlider(globals, dialog, kLeftVolumeSlider);
- }
- else
- {
- globals->rightVolume = volume;
- UpdateSlider(globals, dialog, kRightVolumeSlider);
- }
-
- SetVolume(globals->sndChannel, globals->leftVolume, globals->rightVolume);
- break;
-
- case kRateEdit:
- itemHandle = GetItemHandle(dialog, kRateEdit);
- GetIText((Handle) itemHandle, stringData);
-
- p2cstr(stringData);
- sscanf((char *) stringData, "%f", &rate);
- globals->rateMultiplier = rate * 65536L;
- if (globals->rateMultiplier > kRateEditMax)
- globals->rateMultiplier = kRateEditMax;
-
- SetRateMultiplier(globals->sndChannel, globals->rateMultiplier);
- UpdateSlider(globals, dialog, kRateSlider);
- break;
-
- case kSizeEdit:
- itemHandle = GetItemHandle(dialog, kSizeEdit);
- GetIText((Handle) itemHandle, stringData);
- StringToNum(stringData, (long *) &chunkSamples);
-
- chunkSamples = (chunkSamples / globals->compInfo.samplesPerPacket) * globals->compInfo.samplesPerPacket;
- if (chunkSamples < globals->minChunkSamples)
- chunkSamples = globals->minChunkSamples;
- else if (chunkSamples > globals->totalSamples)
- chunkSamples = globals->totalSamples;
-
- globals->chunkSamples = chunkSamples; // change value all at once 'cause it's used at interrupt time
- UpdateSlider(globals, dialog, kSizeSlider);
- break;
-
- case kLerpCheckBox:
- lerpOn = GetCheckBox(dialog, kLerpCheckBox);
- lerpOn = (lerpOn) ? 0 : 1;
-
- SetLinearInterpolation(globals->sndChannel, lerpOn);
- SetCheckBox(dialog, kLerpCheckBox, lerpOn);
- break;
- }
- }
-
- DoStop(globals->sndChannel);
-
- DisposeDialog(dialog);
- SetPort(oldPort);
-
- NewDialogFailed:
- HUnlock(globals->sndH);
- MatchingSoundNotFound:
- GetSettingsFailed:
- SndDisposeChannel(globals->sndChannel, true);
- SndNewChannelFailed:
- SndVersionFailed:
- return;
- }
-
- /* HasValidSoundManager
-
- This routine checks the Sound Manager version and makes sure the version is greater
- than or equal to the version needed for this program to run. It returns true if
- the version is ok, and false it it is not.
- */
-
-
- Boolean HasValidSoundManager(void)
- {
- NumVersion version;
-
- version = SndSoundManagerVersion(); // get the Sound Manager version
-
- if ((version.majorRev > kRequiredSndMgrMajorRev) || // make sure this version is >= version we need
- ((version.majorRev == kRequiredSndMgrMajorRev) && (version.minorAndBugRev >= kRequiredSndMgrMinorRev)))
- return (true);
- else
- return (false);
- }
-
- /* ParseSnd
-
- This routine parses an 'snd ' resource and returns information about the sound format.
- It uses GetSoundHeaderOffset to find the sound header data structure in the resource,
- parses the sound header and then uses GetCompressionInfo to get the data format and
- convert frames into samples. It returns the sound and compression info, as well as
- offsets in the source resource of the sound header and the start of the audio sample data.
- */
-
- OSErr ParseSnd(Handle sndH, SoundComponentData *sndInfo, CompressionInfo *compInfo,
- unsigned long *headerOffsetResult, unsigned long *dataOffsetResult)
- {
- CommonSoundHeaderPtr sh;
- unsigned long headerOffset, dataOffset;
- short compressionID;
- OSErr err;
-
- // use GetSoundHeaderOffset to find the offset of sound header
- // from the beginning of the sound resource handle
-
- err = GetSoundHeaderOffset((SndListHandle) sndH, (long *) &headerOffset);
- if (err != noErr)
- return (err);
-
- // get pointer to sound header using this offset
-
- sh = (CommonSoundHeaderPtr) (*sndH + headerOffset);
- dataOffset = headerOffset;
-
- // extract the sound info based on encode type
-
- switch (sh->s.encode)
- {
- case stdSH:
- sndInfo->sampleCount = sh->s.length;
- sndInfo->sampleRate = sh->s.sampleRate;
- sndInfo->sampleSize = 8;
- sndInfo->numChannels = 1;
- dataOffset += offsetof(SoundHeader, sampleArea);
- compressionID = notCompressed;
- break;
-
- case extSH:
- sndInfo->sampleCount = sh->e.numFrames;
- sndInfo->sampleRate = sh->e.sampleRate;
- sndInfo->sampleSize = sh->e.sampleSize;
- sndInfo->numChannels = sh->e.numChannels;
- dataOffset += offsetof(ExtSoundHeader, sampleArea);
- compressionID = notCompressed;
- break;
-
- case cmpSH:
- sndInfo->sampleCount = sh->c.numFrames;
- sndInfo->sampleRate = sh->c.sampleRate;
- sndInfo->sampleSize = sh->c.sampleSize;
- sndInfo->numChannels = sh->c.numChannels;
- dataOffset += offsetof(CmpSoundHeader, sampleArea);
- compressionID = sh->c.compressionID;
- sndInfo->format = sh->c.format;
- break;
-
- default:
- return (badFormat);
- break;
- }
-
- // use GetCompressionInfo to get the data format of the
- // sound and the compression information
-
- compInfo->recordSize = sizeof(CompressionInfo);
- err = GetCompressionInfo(compressionID, sndInfo->format, sndInfo->numChannels,
- sndInfo->sampleSize, compInfo);
- if (err != noErr)
- return (err);
-
- // store the sound data format and convert frames to samples
-
- sndInfo->format = compInfo->format;
- sndInfo->sampleCount *= compInfo->samplesPerPacket;
-
- // return offset of header and audio data
-
- *headerOffsetResult = headerOffset;
- *dataOffsetResult = dataOffset;
-
- return (noErr);
- }
-
- /* GetHardwareSettings
-
- This routine uses the SndGetInfo call to return information about the
- sound hardware, including number of channels, sampling rate and sample size.
- It returns this information in a SoundComponentData structure.
- */
-
- OSErr GetHardwareSettings(SndChannelPtr chan, SoundComponentData *hardwareInfo)
- {
- OSErr err;
-
- err = SndGetInfo(chan, siNumberChannels, &hardwareInfo->numChannels);
- if (err != noErr)
- return (err);
-
- err = SndGetInfo(chan, siSampleRate, &hardwareInfo->sampleRate);
- if (err != noErr)
- return (err);
-
- err = SndGetInfo(chan, siSampleSize, &hardwareInfo->sampleSize);
- if (err != noErr)
- return (err);
-
- if (hardwareInfo->sampleSize == 8)
- hardwareInfo->format = kOffsetBinary;
- else
- hardwareInfo->format = kTwosComplement;
-
- return (noErr);
- }
-
- /* FindMatchingSound
-
- This routines tries to find a sound resource whose format matches the
- current sound hardware settings. If a match is found, the globals are
- set up to describe the sound. A sound that matches the hardware settings
- exactly will play most efficiently, requiring less of a CPU load.
- */
-
- OSErr FindMatchingSound(GlobalsPtr globals)
- {
- OSErr err;
- short index;
-
- // loop over all sound resources in this application
-
- index = 1;
- while (globals->sndH = Get1IndResource('snd ', index))
- {
- // parse sound resource to get information about the audio data
-
- err = ParseSnd(globals->sndH, &globals->sndInfo, &globals->compInfo, &globals->headerOffset, &globals->dataOffset);
- if (err != noErr)
- continue;
-
- // compare sample size and number of channels of this sound to the hardware settings
-
- if ((globals->sndInfo.sampleSize == globals->hardwareInfo.sampleSize) &&
- (globals->sndInfo.numChannels == globals->hardwareInfo.numChannels))
- {
- return (noErr); // found a match, so use this sound
- }
-
- // match not found, so release this resource and get the next one
-
- ReleaseResource(globals->sndH);
- index += 1;
- }
-
- return (resNotFound); // no matching resource found
- }
-
- /* GetCompressionName
-
- This routine returns the name of the audio decompression codec specified
- by the OSType parameter. It calls the Component Manager for the codec name
- and returns it as a string. If this codec type is not found, an error will be
- returned.
- */
-
- OSErr GetCompressionName(OSType compressionType, Str255 compressionName)
- {
- ComponentDescription cd;
- Component component;
- Handle componentName;
- OSErr err;
-
- // look for decompressor component
-
- cd.componentType = kSoundDecompressor;
- cd.componentSubType = compressionType;
- cd.componentManufacturer = 0;
- cd.componentFlags = 0;
- cd.componentFlagsMask = 0;
-
- component = FindNextComponent(nil, &cd);
- if (component == nil)
- {
- err = siInvalidCompression;
- goto FindComponentFailed;
- }
-
- // create handle for name
-
- componentName = NewHandle(0);
- if (componentName == nil)
- {
- err = MemError();
- goto NewNameFailed;
- }
-
- // get name from Component Manager
-
- err = GetComponentInfo(component, &cd, componentName, nil, nil);
- if (err != noErr)
- goto GetInfoFailed;
-
- // return name
-
- BlockMoveData(*componentName, compressionName, GetHandleSize(componentName));
-
- GetInfoFailed:
- DisposeHandle(componentName);
- NewNameFailed:
- FindComponentFailed:
- return (err);
- }
-
- /* DoCallback
-
- This Pascal-type routine is called when the Sound Manager executes a callBackCmd.
- We passed a pointer to this routine when we opened the channel with SndNewChannel.
- We are passed the sound channel and the sound command used when we issued the
- callBackCmd. The param2 field of this command contains a user-defined value, into
- which we have placed a pointer to our globals. We use this pointer to call DoPlay,
- which actually handles the interrupt.
-
- Remember, this routine is called at interrupt time, so A5 is not
- set up and you cannot call the Memory Manager or most other toolbox routines.
- */
-
- pascal void DoCallback(SndChannelPtr chan, SndCommand *cmd)
- {
- DoPlay((GlobalsPtr) cmd->param2);
- }
-
- /* DoPlay
-
- This routine uses a bufferCmd/callBackCmd scheme to play consecutive chunks a
- sound resource. If the end of the sound data is reached, playback wraps around
- to the beginning of the sound.
-
- This routine is called once at non-interrupt time to start the sound playing,
- and then will be called repeatedly at interrupt time in response to the callBackCmd
- (as described above in the DoCallback routine). For this reason, we cannot assume A5
- will be set up, nor can we call most toolbox routines. A pointer to our globals is
- passed in so we can keep track of things.
- */
-
- OSErr DoPlay(GlobalsPtr globals)
- {
- CommonSoundHeaderPtr sh;
- SndCommand sndCmd;
- unsigned long chunkSamples, frames;
- Ptr samplePtr;
- OSErr err;
-
- // wrap around to beginning of buffer if end has been reached
-
- if (globals->sampleOffset == globals->totalSamples)
- globals->sampleOffset = 0;
-
- // determine size of chunk to play, in samples
-
- chunkSamples = globals->totalSamples - globals->sampleOffset;
- if (chunkSamples > globals->chunkSamples)
- chunkSamples = globals->chunkSamples;
-
- // calculate pointer to audio data by converting the sample offset to bytes
- // and adding it to address of the beginning of the audio samples.
-
- samplePtr = *globals->sndH + globals->dataOffset;
- samplePtr += (globals->sampleOffset / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame;
- globals->sampleOffset += chunkSamples;
-
- // setup fields in sound header for the address of the audio samples and
- // the number of frames.
-
- frames = chunkSamples / globals->compInfo.samplesPerPacket;
-
- switch (globals->sndHeader.s.encode)
- {
- case stdSH:
- globals->sndHeader.s.samplePtr = samplePtr;
- globals->sndHeader.s.length = frames;
- break;
-
- case extSH:
- globals->sndHeader.e.samplePtr = samplePtr;
- globals->sndHeader.e.numFrames = frames;
- break;
-
- case cmpSH:
- globals->sndHeader.c.samplePtr = samplePtr;
- globals->sndHeader.c.numFrames = frames;
- break;
- }
-
- // issue bufferCmd to play the sound, using SndDoImmediate
-
- sndCmd.cmd = bufferCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (long) &globals->sndHeader;
-
- err = SndDoImmediate(globals->sndChannel, &sndCmd);
- if (err != noErr)
- return (err);
-
- // issue callBackCmd using SndDoCommand so we get called
- // back when the buffer is done playing.
-
- sndCmd.cmd = callBackCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (long) globals;
-
- err = SndDoCommand(globals->sndChannel, &sndCmd, true);
- return (err);
- }
-
- /* DoStop
-
- This routine is used to stop sound playing on the channel. It is important
- to flush the queue before sending the quietCmd, because any commands left
- in the queue will begin executing when the quietCmd is received.
- */
-
- OSErr DoStop(SndChannelPtr chan)
- {
- SndCommand sndCmd;
- OSErr err;
-
- sndCmd.cmd = flushCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = 0;
-
- err = SndDoImmediate(chan, &sndCmd);
- if (err != noErr)
- return (err);
-
- sndCmd.cmd = quietCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = 0;
-
- err = SndDoImmediate(chan, &sndCmd);
- return (err);
- }
-
- /* SetRateMultiplier
-
- This routine sets the rate multiplier for a channel.
- */
-
- OSErr SetRateMultiplier(SndChannelPtr chan, UnsignedFixed rateMultiplier)
- {
- SndCommand sndCmd;
- OSErr err;
-
- sndCmd.cmd = rateMultiplierCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = rateMultiplier;
-
- err = SndDoImmediate(chan, &sndCmd);
- return (err);
- }
-
- /* GetRateMultiplier
-
- This routine returns the rate multiplier for a channel.
- */
-
- OSErr GetRateMultiplier(SndChannelPtr chan, UnsignedFixed *rateMultiplier)
- {
- SndCommand sndCmd;
- OSErr err;
-
- sndCmd.cmd = getRateMultiplierCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (long) rateMultiplier;
-
- err = SndDoImmediate(chan, &sndCmd);
- return (err);
- }
-
- /* SetVolume
-
- This routine sets the left and right volumes for a channel.
- */
-
- OSErr SetVolume(SndChannelPtr chan, unsigned short leftVolume, unsigned short rightVolume)
- {
- SndCommand sndCmd;
- OSErr err;
-
- sndCmd.cmd = volumeCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (rightVolume << 16) | leftVolume;
-
- err = SndDoImmediate(chan, &sndCmd);
- return (err);
- }
-
- /* GetVolume
-
- This routine returns the left and right volumes for a channel.
- */
-
- OSErr GetVolume(SndChannelPtr chan, unsigned short *leftVolume, unsigned short *rightVolume)
- {
- SndCommand sndCmd;
- unsigned long volume;
- OSErr err;
-
- sndCmd.cmd = getVolumeCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (long) &volume;
-
- err = SndDoImmediate(chan, &sndCmd);
-
- if (err == noErr)
- {
- *leftVolume = volume & 0x0000FFFF;
- *rightVolume = volume >> 16;
- }
- return (err);
- }
-
- /* SetLinearInterpolation
-
- This routine turns linear interpolation rate conversion on and off for a channel.
- */
-
- OSErr SetLinearInterpolation(SndChannelPtr chan, short doLerp)
- {
- SndCommand sndCmd;
- OSErr err;
-
- sndCmd.cmd = reInitCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (doLerp) ? 0 : initNoInterp;
-
- err = SndDoImmediate(chan, &sndCmd);
- return (err);
- }
-
- /* Dialog Routines
-
- Remember, this application is meant to show cool things you can do with the
- Sound Manager, not to show good human interface programming. The following
- code to manage the dialog and handle the sliders is pretty crude, and is not
- recommended for general application use. It is definitely not localizable!
- */
-
- void SetupDialog(GlobalsPtr globals, DialogPtr dialog)
- {
- Str255 stringData;
- ControlHandle itemHandle;
- OSErr err;
-
- /* setup left volume edit box and slider */
-
- sprintf((char *) stringData, "%7.5f", ((float) globals->leftVolume) / 256.0);
- c2pstr((char *) stringData);
- itemHandle = GetItemHandle(dialog, kLeftVolumeEdit);
- SetIText((Handle) itemHandle, stringData);
- SetItemProc(dialog, kLeftVolumeSlider, DrawSlider);
-
- /* setup right volume edit box and slider */
-
- sprintf((char *) stringData, "%7.5f", ((float) globals->rightVolume) / 256.0);
- c2pstr((char *) stringData);
- itemHandle = GetItemHandle(dialog, kRightVolumeEdit);
- SetIText((Handle) itemHandle, stringData);
- SetItemProc(dialog, kRightVolumeSlider, DrawSlider);
-
- /* setup rate multiplier edit box and slider */
-
- sprintf((char *) stringData, "%7.5f", ((float) globals->rateMultiplier) / 65536.0);
- c2pstr((char *) stringData);
- itemHandle = GetItemHandle(dialog, kRateEdit);
- SetIText((Handle) itemHandle, stringData);
- SetItemProc(dialog, kRateSlider, DrawSlider);
-
- /* setup buffer size edit box and slider */
-
- NumToString(globals->chunkSamples, stringData);
- itemHandle = GetItemHandle(dialog, kSizeEdit);
- SetIText((Handle) itemHandle, stringData);
- SetItemProc(dialog, kSizeSlider, DrawSlider);
-
- /* setup hardware settings label */
-
- SoundSettings2String(&globals->hardwareInfo, stringData);
- itemHandle = GetItemHandle(dialog, kHardwareSettingsLabel);
- SetIText((Handle) itemHandle, stringData);
-
- /* setup sound settings label */
-
- SoundSettings2String(&globals->sndInfo, stringData);
- itemHandle = GetItemHandle(dialog, kSoundSettingsLabel);
- SetIText((Handle) itemHandle, stringData);
-
- /* setup linear interpolation checkbox */
-
- SetCheckBox(dialog, kLerpCheckBox, 1);
-
- /* set the default button */
-
- SetDialogDefaultItem(dialog, kQuitButton);
- }
-
- void SoundSettings2String(SoundComponentData *sndInfo, Str255 stringData)
- {
- float rate;
- Str255 dataFormat;
- OSErr err;
-
- if ((sndInfo->format == kOffsetBinary) ||
- (sndInfo->format == kTwosComplement))
- {
- sprintf((char *) dataFormat, "%d-bit", sndInfo->sampleSize);
- }
- else
- {
- err = GetCompressionName(sndInfo->format, dataFormat);
- if (err != noErr)
- sprintf((char *) dataFormat, "Unknown");
- else
- p2cstr(dataFormat);
- }
-
- rate = ((double) sndInfo->sampleRate) / (65536.0 * 1000.0);
-
- sprintf((char *) stringData, "%s, %s, %5.3f kHz",
- dataFormat,
- (sndInfo->numChannels == 1) ? "mono" : "stereo",
- rate);
- c2pstr((char *) stringData);
- }
-
- void GetThumbRect(GlobalsPtr globals, short item, Rect *bounds, Rect *thumbRect)
- {
- Rect r;
-
- r = *bounds;
- InsetRect(&r, 4, 0);
-
- switch (item)
- {
- case kLeftVolumeSlider:
- {
- unsigned long volume = globals->leftVolume;
- if (volume > kVolumeSliderMax)
- volume = kVolumeSliderMax;
-
- r.left = r.right - 4 - ((kVolumeSliderMax - volume) * (r.right - r.left)) / kVolumeSliderMax;
- }
- break;
-
- case kRightVolumeSlider:
- {
- unsigned long volume = globals->rightVolume;
- if (volume > kVolumeSliderMax)
- volume = kVolumeSliderMax;
-
- r.left = r.right - 4 - ((kVolumeSliderMax - volume) * (r.right - r.left)) / kVolumeSliderMax;
- }
- break;
-
- case kRateSlider:
- {
- UnsignedFixed rateMultiplier = globals->rateMultiplier;
- if (rateMultiplier > kRateSliderMax)
- rateMultiplier = kRateSliderMax;
-
- r.left = r.right - 4 - ((kRateSliderMax - rateMultiplier) * (r.right - r.left)) / kRateSliderMax;
- }
- break;
-
- case kSizeSlider:
- {
- unsigned long chunkSamples = globals->chunkSamples;
- if (chunkSamples > globals->totalSamples)
- chunkSamples = globals->totalSamples;
-
- r.left = r.right - 4 - ((globals->totalSamples - chunkSamples) * (r.right - r.left)) / globals->totalSamples;
- }
- break;
- }
-
- r.right = r.left + 8;
- *thumbRect = r;
- }
-
- pascal void DrawSlider(WindowPtr dialog, short item)
- {
- UpdateSlider(&myGlobals, dialog, item);
- }
-
- void UpdateSlider(GlobalsPtr globals, DialogPtr dialog, short item)
- {
- Rect r, bounds;
-
- GetItemRect(dialog, item, &bounds);
- EraseRect(&bounds);
-
- r = bounds;
- InsetRect(&r, 4, 4);
- FrameRect(&r);
- InsetRect(&r, 1, 1);
- FillRect(&r, &qd.gray);
-
- GetThumbRect(globals, item, &bounds, &r);
-
- EraseRect(&r);
- FrameRect(&r);
- }
-
- void DragSlider(GlobalsPtr globals, DialogPtr dialog, short item)
- {
- Rect r, bounds, thumbRect;
- Point oldMouse, newMouse;
- ControlHandle itemHandle;
- Str255 stringData;
-
- if (!StillDown())
- return;
-
- GetItemRect(dialog, item, &bounds);
-
- r = bounds;
- InsetRect(&r, 4, 4);
- itemHandle = GetItemHandle(dialog, item + 1);
-
- GetThumbRect(globals, item, &bounds, &thumbRect);
- oldMouse.h = (thumbRect.left + thumbRect.right) / 2;
- oldMouse.v = (thumbRect.top + thumbRect.bottom) / 2;
-
- while (WaitMouseUp())
- {
- GetMouse(&newMouse);
-
- if (newMouse.h < r.left)
- newMouse.h = r.left;
- if (newMouse.h > r.right)
- newMouse.h = r.right;
-
- if (EqualPt(newMouse, oldMouse))
- continue;
-
- switch (item)
- {
- case kLeftVolumeSlider:
- case kRightVolumeSlider:
- {
- unsigned long volume = ((newMouse.h - r.left) * kVolumeSliderMax) / (r.right - r.left);
- sprintf((char *) stringData, "%7.5f", ((float) volume) / 256.0);
- c2pstr((char *) stringData);
-
- if (item == kLeftVolumeSlider)
- globals->leftVolume = volume;
- else
- globals->rightVolume = volume;
-
- SetVolume(globals->sndChannel, globals->leftVolume, globals->rightVolume);
- }
- break;
-
- case kRateSlider:
- {
- globals->rateMultiplier = ((newMouse.h - r.left) * kRateSliderMax) / (r.right - r.left);
- sprintf((char *) stringData, "%7.5f", ((float) globals->rateMultiplier) / 65536.0);
- c2pstr((char *) stringData);
- SetRateMultiplier(globals->sndChannel, globals->rateMultiplier);
- }
- break;
-
- case kSizeSlider:
- {
- unsigned long chunkSamples = ((newMouse.h - r.left) * globals->totalSamples) / (r.right - r.left);
- chunkSamples = (chunkSamples / globals->compInfo.samplesPerPacket) * globals->compInfo.samplesPerPacket;
- if (chunkSamples < globals->minChunkSamples)
- chunkSamples = globals->minChunkSamples;
- globals->chunkSamples = chunkSamples; // change value all at once 'cause it's used at interrupt time
- NumToString(globals->chunkSamples, stringData);
- }
- break;
- }
-
- EraseRect(&thumbRect);
- FrameRect(&r);
- InsetRect(&r, 1, 1);
- FillRect(&r, &qd.gray);
- InsetRect(&r, -1, -1);
-
- GetThumbRect(globals, item, &bounds, &thumbRect);
- EraseRect(&thumbRect);
- FrameRect(&thumbRect);
-
- SetIText((Handle) itemHandle, stringData);
-
- oldMouse = newMouse;
- }
- }
-
- // Here are some useful routines for dealing with dialogs
-
- ControlHandle GetItemHandle(DialogPtr dialog, short item)
- {
- short iKind;
- Rect iRect;
- Handle iHandle;
-
- GetDItem(dialog, item, &iKind, &iHandle, &iRect);
- return ((ControlHandle)iHandle);
- }
-
- void SetItemProc(DialogPtr dialog, short item, UserItemUPP proc)
- {
- short iKind;
- Rect iRect;
- Handle iHandle;
-
- GetDItem(dialog, item, &iKind, &iHandle, &iRect);
- SetDItem(dialog, item, iKind, (Handle) proc, &iRect);
- }
-
- void GetItemRect(DialogPtr dialog, short item, Rect *iRect)
- {
- short iKind;
- Handle iHandle;
-
- GetDItem(dialog, item, &iKind, &iHandle, iRect);
- }
-
- short GetCheckBox(DialogPtr dialog, short item)
- {
- short iKind;
- Rect iRect;
- Handle iHandle;
-
- GetDItem(dialog, item, &iKind, &iHandle, &iRect);
- return (GetCtlValue((ControlHandle) iHandle));
- }
-
- void SetCheckBox(DialogPtr dialog, short item, short value)
- {
- short iKind;
- Rect iRect;
- Handle iHandle;
-
- GetDItem(dialog, item, &iKind, &iHandle, &iRect);
- SetCtlValue((ControlHandle) iHandle, (value) ? 1 : 0);
- }
-